/*
 * ESequenceInputStream.hh
 *
 *  Created on: 2013-7-24
 *      Author: cxxjava@163.com
 */

#ifndef ESEQUENCEINPUTSTREAM_HH_
#define ESEQUENCEINPUTSTREAM_HH_

#include "EInputStream.hh"
#include "EEnumeration.hh"
#include "EIOException.hh"
#include "EVector.hh"

namespace efc {

/**
 * A <code>SequenceInputStream</code> represents
 * the logical concatenation of other input
 * streams. It starts out with an ordered
 * collection of input streams and reads from
 * the first one until end of file is reached,
 * whereupon it reads from the second one,
 * and so on, until end of file is reached
 * on the last of the contained input streams.
 *
 * @version 1.33, 06/07/06
 * @since   JDK1.0
 */

class ESequenceInputStream: public EInputStream {
public:
	virtual ~ESequenceInputStream();

	/**
	 * Initializes a newly created <code>SequenceInputStream</code>
	 * by remembering the argument, which must
	 * be an <code>Enumeration</code>  that produces
	 * objects whose run-time type is <code>InputStream</code>.
	 * The input streams that are  produced by
	 * the enumeration will be read, in order,
	 * to provide the bytes to be read  from this
	 * <code>SequenceInputStream</code>. After
	 * each input stream from the enumeration
	 * is exhausted, it is closed by calling its
	 * <code>close</code> method.
	 *
	 * @param   e   an enumeration of input streams.
	 * @see     java.util.Enumeration
	 */
	ESequenceInputStream(EEnumeration<EInputStream*>* e);

	/**
	 * Initializes a newly
	 * created <code>SequenceInputStream</code>
	 * by remembering the two arguments, which
	 * will be read in order, first <code>s1</code>
	 * and then <code>s2</code>, to provide the
	 * bytes to be read from this <code>SequenceInputStream</code>.
	 *
	 * @param   s1   the first input stream to read.
	 * @param   s2   the second input stream to read.
	 */
	ESequenceInputStream(EInputStream* s1, EInputStream* s2);

	/**
	 * Returns an estimate of the number of bytes that can be read (or
	 * skipped over) from the current underlying input stream without
	 * blocking by the next invocation of a method for the current
	 * underlying input stream. The next invocation might be
	 * the same thread or another thread.  A single read or skip of this
	 * many bytes will not block, but may read or skip fewer bytes.
	 * <p>
	 * This method simply calls {@code available} of the current underlying
	 * input stream and returns the result.
	 *
	 * @return an estimate of the number of bytes that can be read (or
	 *         skipped over) from the current underlying input stream
	 *         without blocking or {@code 0} if this input stream
	 *         has been closed by invoking its {@link #close()} method
	 * @exception  IOException  if an I/O error occurs.
	 *
	 * @since   JDK1.1
	 */
	virtual long available() THROWS(EIOException);

	/**
	 * Reads the next byte of data from this input stream. The byte is
	 * returned as an <code>int</code> in the range <code>0</code> to
	 * <code>255</code>. If no byte is available because the end of the
	 * stream has been reached, the value <code>-1</code> is returned.
	 * This method blocks until input data is available, the end of the
	 * stream is detected, or an exception is thrown.
	 * <p>
	 * This method
	 * tries to read one character from the current substream. If it
	 * reaches the end of the stream, it calls the <code>close</code>
	 * method of the current substream and begins reading from the next
	 * substream.
	 *
	 * @return     the next byte of data, or <code>-1</code> if the end of the
	 *             stream is reached.
	 * @exception  IOException  if an I/O error occurs.
	 */
	virtual int read() THROWS(EIOException);

	/**
	 * Reads up to <code>len</code> bytes of data from this input stream
	 * into an array of bytes.  If <code>len</code> is not zero, the method
	 * blocks until at least 1 byte of input is available; otherwise, no
	 * bytes are read and <code>0</code> is returned.
	 * <p>
	 * The <code>read</code> method of <code>SequenceInputStream</code>
	 * tries to read the data from the current substream. If it fails to
	 * read any characters because the substream has reached the end of
	 * the stream, it calls the <code>close</code> method of the current
	 * substream and begins reading from the next substream.
	 *
	 * @param      b     the buffer into which the data is read.
	 * @param      off   the start offset in array <code>b</code>
	 *                   at which the data is written.
	 * @param      len   the maximum number of bytes read.
	 * @return     int   the number of bytes read.
	 * @exception  NullPointerException If <code>b</code> is <code>null</code>.
	 * @exception  IndexOutOfBoundsException If <code>off</code> is negative,
	 * <code>len</code> is negative, or <code>len</code> is greater than
	 * <code>b.length - off</code>
	 * @exception  IOException  if an I/O error occurs.
	 */
	virtual int read(void *b, int len) THROWS(EIOException);

	/**
	 * Closes this input stream and releases any system resources
	 * associated with the stream.
	 * A closed <code>SequenceInputStream</code>
	 * cannot  perform input operations and cannot
	 * be reopened.
	 * <p>
	 * If this stream was created
	 * from an enumeration, all remaining elements
	 * are requested from the enumeration and closed
	 * before the <code>close</code> method returns.
	 *
	 * @exception  IOException  if an I/O error occurs.
	 */
	virtual void close() THROWS(EIOException);

protected:
	EEnumeration<EInputStream*>* _e;
	boolean _eOwned;
	EInputStream* _in;

	/**
	 *  Continues reading in the next stream if an EOF is reached.
	 */
	void nextStream() THROWS(EIOException);

private:
	EVector<EInputStream*>* _v;
};

} /* namespace efc */
#endif /* ESEQUENCEINPUTSTREAM_HH_ */
